package dao

import (
	"context"
	"errors"
	"github.com/qiniu/qmgo"
	"github.com/qiniu/qmgo/options"
	"github.com/sirupsen/logrus"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	officialOpts "go.mongodb.org/mongo-driver/mongo/options"
	"monoblog/config"
	"monoblog/util"
	"sync"
)

var (
	sequenceDao *AutoIDSequenceDao // 单例实例存储变量
	once0       sync.Once          // 确保初始化只执行一次的同步工具
)

// 初始化 QMGO 集合（单例模式）
func NewAutoIDSequenceDao() *AutoIDSequenceDao {
	once0.Do(func() { // 并发安全的初始化逻辑，只会执行一次
		sequenceDao = &AutoIDSequenceDao{
			collection: config.GetDataBase().Collection(AutoIDSequenceCollectionName), //指定集合名称
			ctx:        context.Background(),
		}
	})
	return sequenceDao // 返回唯一实例
}

type AutoIDSequenceDao struct {
	collection *qmgo.Collection // QMGO 集合对象（替代原生 mongo.Collection）
	ctx        context.Context  // 上下文（用于数据库操作超时控制）
}

// NextID - 获取自增ID
func (s *AutoIDSequenceDao) NextID(sequenceName string) (int64, error) {
	dn, err := s.NextIDn(sequenceName, 1)

	if len(dn) > 0 {
		return dn[0], nil
	} else {
		return 0, err
	}
}

// NextIDn - 获取自增ID
// size 预取的数量
func (s *AutoIDSequenceDao) NextIDn(sequenceName string, size int) ([]int64, error) {
	if size < 1 {
		return nil, errors.New("size must be greater than 0")
	}
	updateDoc := bson.M{ // 仅在插入新文档时执行（upsert 触发）
		"$inc":         bson.M{"seq": size}, // 文档存在时：seq 自增 1
		"$setOnInsert": bson.M{},
	}

	opts := options.UpdateOptions{
		UpdateOptions: officialOpts.Update().SetUpsert(true),
	}

	err := s.collection.UpdateId(s.ctx, sequenceName, updateDoc, opts)
	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Infof("record setOnInsert. autoId sequence id: %v", sequenceName)

		return util.GetSequenceInt64(int64(size), size), nil
	}
	if err == nil {
		currId, _ := s.currentID(sequenceName)
		return util.GetSequenceInt64(currId, size), nil
	}

	logrus.Errorf("update id sequence failed. id: %v, err: %v", sequenceName, err)
	return nil, err
}

func (s *AutoIDSequenceDao) currentID(sequenceName string) (int64, error) {
	var idSequence AutoIDSequence

	err := s.collection.Find(s.ctx, qmgo.M{"_id": sequenceName}).One(&idSequence)

	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. id: %v", sequenceName)
		return 0, errors.New("找不到数据, sequence: " + sequenceName)
	}
	return idSequence.Seq, err
}

// AutoIDSequence 自增ID序列
type AutoIDSequence struct {
	ID  string `json:"id" bson:"_id,omitempty"` // ID
	Seq int64  `json:"seq" bson:"seq"`
}

const AutoIDSequenceCollectionName = "auto_id_sequence" // 集合名常量
